/** @file md5_model.cpp
    @brief MD5 model */

#include "md5/md5_object.h"

using namespace G3;

//==============================================================================
// Constructor(s)
//==============================================================================
G3::MD5_MODEL::MD5_MODEL () {
    baseSkeleton = NULL;

    numJoints = 0;
    numMeshes = 0;

    log = &G3::MainLog;
}

//==============================================================================
// Destructor(s)
//==============================================================================
G3::MD5_MODEL::~MD5_MODEL () {
    if (!meshes.empty ())
        meshes.clear ();
    if (!animationMap.empty ())
        animationMap.clear ();
}

//==============================================================================
// Loads a *.md5_mesh file
//==============================================================================
bool G3::MD5_MODEL::LoadMesh (const std::string &filename) {
    std::string path = G3::MainFilesys.GetPath (filename.c_str ());
    G3::STREAM *stream = G3::MainFileHandler.ReadWholeFile (path);
    bool retval = false;

    if (stream) {
        retval = ParseMesh (stream);

        for (int i=0; i<(int) meshes.size (); i++) {
            meshes [i]->SetupTextures ();

            meshes [i]->SetupIndices ();
            meshes [i]->CalcBBox (baseSkeleton);
            meshes [i]->CalcWeightNormals (baseSkeleton);
            meshes [i]->CalcWeightTangents (baseSkeleton);
        }

        return retval;
    }
    return false;
}

//==============================================================================
// Loads a *.md5_anim file
//==============================================================================
bool G3::MD5_MODEL::LoadAnimation (const std::string &filename, const std::string &animName) {
    std::string path = G3::MainFilesys.GetPath (filename.c_str ());
    G3::STREAM *stream = G3::MainFileHandler.ReadWholeFile (path);
    std::map<std::string, ANIMATION *>::iterator it;
    ANIMATION *anim = NULL;
    bool retval = false;

    if (stream) {
        anim = new ANIMATION ();

        if (!anim) {
            if (log)
                log->Report ("ERROR! MD5_MODEL::LoadAnimation: Failed to allocate memory for an animation..\n");
            return false;
        }

        // Only allow one animation for every name
        it = animationMap.find (animName);
        if (it != animationMap.end ())
            animationMap.erase (it);

        animationMap.insert (std::pair<std::string, ANIMATION *> (animName, anim));

        anim->name = animName;

        retval = anim->ParseAnimation (stream);

        return retval;
    }
    return false;
}

// --------------------------------------------------------------------------
// Check if an animation is valid for this model or not.  A valid
// animation must have a skeleton with the same number of joints with
// model's skeleton and for each skeleton joint, name and parent Id must
// match.
// --------------------------------------------------------------------------
//==============================================================================
// Checks the animation and mesh for consistency
//==============================================================================
bool G3::MD5_MODEL::ValidityCheck (ANIMATION *anim) const {
    if (!anim)
        return false;

    if (anim->skeletonFrames.size () < 1 ||
            numJoints != (int) anim->skeletonFrames [0]->joints.size ())
        return false;

    JOINT *mJoint = NULL, *aJoint = NULL;

    for (int i=0; i<numJoints; i++) {
        mJoint = baseSkeleton->joints [i];
        aJoint = anim->skeletonFrames [0]->joints [i];

        if (mJoint->name != aJoint->name)
            return false;
        if (mJoint->parent != aJoint->parent)
            return false;
    }

    return true;
}

//==============================================================================
// Gets a mesh pointer by name
//==============================================================================
MD5_MODEL::MESH *G3::MD5_MODEL::GetMesh (const std::string &meshName) {
    for (int i=0; i<numMeshes; i++) {
        if (meshes [i]->name == meshName)
            return meshes [i];
    }

    return NULL;
}

//==============================================================================
// Gets an animation pointer by name
//==============================================================================
MD5_MODEL::ANIMATION *G3::MD5_MODEL::GetAnimation (const std::string &animName) {
    std::map<std::string, ANIMATION *>::iterator it;

    it = animationMap.find (animName);

    if (it != animationMap.end ())
        return it->second;
    return NULL;
}

//==============================================================================
// Prepares the model for rendering
//==============================================================================
void G3::MD5_MODEL::Prepare (SKELETON *skeleton) {
    for (int i=0; i<(int) meshes.size (); i++) {
        if (meshes [i]->visible)
            meshes [i]->CalcVertices (skeleton);
    }
}

//==============================================================================
// Renders the model
//==============================================================================
void G3::MD5_MODEL::Render () {
    for (int i=0; i<numMeshes; i++)
        meshes [i]->Render ();
}
